Common Mistakes in API Testing
Common Mistakes in API Testing
API testing is a critical part of ensuring application reliability, but even experienced testers can fall into common traps.
This section highlights frequent mistakes and provides practical solutions to avoid them.
1️⃣ Hardcoding Data
The Problem
Hardcoding values like IDs, tokens, or URLs makes tests brittle and environment-specific.
Example
// Brittle test with hardcoded ID
given()
.when()
.get("/users/101")
.then()
.statusCode(200);
Solution
Use dynamic data and environment variables.
Code Snippet: Using Dynamic Data
// Use environment variable for base URL
String baseUrl = System.getenv("BASE_URL");
// Use dynamic user ID
int userId = getUserIdFromDatabase();
given()
.baseUri(baseUrl)
.pathParam("id", userId)
.when()
.get("/users/{id}")
.then()
.statusCode(200);
2️⃣ Ignoring Authentication
The Problem
Skipping authentication or hardcoding tokens leads to insecure and unreliable tests.
Example
// Test without authentication
given()
.when()
.get("/secure-endpoint")
.then()
.statusCode(200);
Solution
Handle authentication dynamically using tokens or OAuth flows.
Code Snippet: Handling Bearer Token
// Authenticate and use token
String token = authenticateUser();
given()
.header("Authorization", "Bearer " + token)
.when()
.get("/secure-endpoint")
.then()
.statusCode(200);
3️⃣ Overlooking Edge Cases
The Problem
Focusing only on happy paths misses critical scenarios like invalid inputs, null values, or boundary conditions.
Example
Testing only valid user creation:
// Happy path test
given()
.body("{ \"name\": \"John\", \"email\": \"john@example.com\" }")
.when()
.post("/users")
.then()
.statusCode(201);
Solution
Validate negative scenarios and edge cases.
Code Snippet: Testing Invalid Input
// Negative test for invalid email
given()
.body("{ \"name\": \"John\", \"email\": \"invalid-email\" }")
.when()
.post("/users")
.then()
.statusCode(400);
4️⃣ Not Validating Database State
The Problem
Relying solely on API responses without verifying the database can lead to false positives.
Example
Testing only the API response:
// Test API response
given()
.when()
.post("/users")
.then()
.statusCode(201);
Solution
Validate both API responses and database records.
Code Snippet: Validating Database
// Query database
String query = "SELECT * FROM users WHERE email = ?";
String email = "john@example.com";
try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setString(1, email);
ResultSet rs = stmt.executeQuery();
// Assert record exists
assertTrue(rs.next(), "User not found in database");
} catch (SQLException e) {
fail("Database query failed: " + e.getMessage());
}
5️⃣ Flaky Tests
The Problem
Flaky tests produce inconsistent results, eroding trust in the test suite.
Causes
- Race conditions.
- Shared test environments.
- Unstable dependencies.
Solution
- Use unique test data.
- Mock external dependencies.
- Retry failed tests selectively.
Code Snippet: Using Unique Test Data
// Generate unique email for each test
String uniqueEmail = "testuser_" + UUID.randomUUID() + "@example.com";
given()
.body("{ \"name\": \"Test\", \"email\": \"" + uniqueEmail + "\" }")
.when()
.post("/users")
.then()
.statusCode(201);
6️⃣ Ignoring CI/CD Integration
The Problem
Running tests manually delays feedback and increases the risk of regressions.
Solution
Integrate tests into CI/CD pipelines for automated execution.
Code Snippet: GitHub Actions Workflow
name: API Test Pipeline
on:
push:
branches:
- main
jobs:
api-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Run API tests
run: mvn clean test
7️⃣ Missing Logging and Debugging
The Problem
Lack of detailed logs makes debugging failures difficult.
Solution
Log requests, responses, and test results for better visibility.
Code Snippet: Enabling Logs in RestAssured
// Log request and response details
given()
.log().all()
.when()
.get("/users/101")
.then()
.log().all()
.statusCode(200);
Key Takeaways 🎯
- Avoid hardcoding data; use dynamic values and environment variables.
- Handle authentication securely and dynamically.
- Validate both happy paths and edge cases.
- Always validate database state for critical workflows.
- Address flaky tests by isolating data and mocking dependencies.
- Integrate tests into CI/CD pipelines for continuous validation.
- Enable detailed logging for easier debugging.